package br.com.afi.encoding_converter;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Collection;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.lang.StringUtils;

import br.com.afi.util.log.Logger;

/**
 * Classe que realiza a conversão de encoding de arquivos.
 * 
 * @author André de Fontana Ignacio
 * @version 1.0
 */
public class EncodingConverter {
	private static final Logger log = Logger.getLogger( EncodingConverter.class );
	public static final String UTF_8 = "UTF-8";
	public static final String ISO8859_1 = "ISO8859-1";
	public OutputStream os;
	
	/**
	 * Construtor padrão. Esse construtor assume System.out como stream de saída
	 */
	public EncodingConverter()
	{
		super();
		os = System.out;
	}
	
	/**
	 * Construtor
	 * 
	 * @param os Stream de saída das mensagens.
	 */
	public EncodingConverter( OutputStream os )
	{
		this.os = os;
	}
	
	/**
	 * Escreve o conteúdo do arquivo na stream de saída da classe.
	 * 
	 * @param file Arquivo
	 * @param encoding Encoding para leitura do arquivo
	 * @throws IOException Lançada quando não for possível ler o arquivo ou escrever na Stream.
	 */
	public void showFile( File file, String encoding ) throws IOException
	{
		final InputStreamReader is =  new InputStreamReader( FileUtils.openInputStream( file ), encoding );
		IOUtils.copy( is, os );
		IOUtils.closeQuietly( is );
	}
	
	/**
	 * Escreve o conteúdo dos arquivos na stream de saída da classe.
	 * 
	 * @param files Arquivos
	 * @param encoding Encoding para leitura do arquivo
	 * @throws IOException Lançada quando não for possível ler os arquivos ou escrever na Stream.
	 */
	public void showFiles( Collection<File> files, String encoding ) throws IOException
	{
		for( File file : files )
		{
			showFile( file, encoding );
		}
	}
	
	/**
	 * Obtém o charset padrão do sistema
	 * 
	 * @return Charset padrão
	 */
	public Charset getDefaultSystemCharset()
	{
		return Charset.defaultCharset();
	}
	
	/**
	 * Apresenta o Charset default na stream de saída classe.
	 * 
	 * @throws IOException Lançada quando não for possível escrever na Stream.
	 */
	public void showDefaultSystemCharset() throws IOException
	{
		IOUtils.write( MessageFormat.format( "Default System Charset: {0}", getDefaultSystemCharset().displayName() ), os );
	}
	
	/**
	 * Obtém o diretório atual do usuário
	 * 
	 * @return Diretório atual do usuário
	 */
	public File getUserDir()
	{
		return new File( System.getProperty( "user.dir" ) ); 
	}
	
	/**
	 * Obtém uma lista de arquivos no diretório informado filtrando pelo nome (aceita wildcards). 
	 * 
	 * @param dir Diretório inicial da busca
	 * @param filename Pattern para filtrar os arquivos, aceita wildcards, ex: *.txt
	 * @param recursive Buscar nos sub-diretórios?
	 * @return Lista de arquivos encontrados
	 */
	@SuppressWarnings("unchecked")
	public Collection<File> getFiles( File dir, String filename, boolean recursive )
	{
		Collection<File> list = null;
		if(recursive)
		{
			list = FileUtils.listFiles(dir, new WildcardFileFilter( filename ), new WildcardFileFilter( "*" ) );
		}
		else
		{
			list = FileUtils.listFiles(dir, new WildcardFileFilter( filename ), null );
		}
		
		return list;
	}
	
	/**
	 * Obtém uma lista de arquivos no diretório informado filtrando pelo nome (aceita wildcards). Não busca nos sub-diretórios.
	 * 
	 * @param dir Diretório inicial da busca
	 * @param filename Pattern para filtrar os arquivos, aceita wildcards, ex: *.txt
	 * @return Lista de arquivos encontrados
	 */
	public Collection<File> getFiles( File dir, String filename )
	{
		return getFiles( dir, filename, false );
	}
	
	/**
	 * Executa a classe de acordo com os comandos.
	 * 
	 * @param command Comando a ser executado
	 * @throws IOException Lançada quando não for possível apresentar as mensagens
	 */
	public void execute( Command command ) throws IOException
	{
		if( command.isShowDefaultSystemCharset() )
		{
			showDefaultSystemCharset();
		}
		final String fileFilterPattern = command.getFileFilterPattern();
		final String toEncoding = command.getToEncoding();
		final String outputDirectory = command.getToDirectory();
		
		String fromEncoding = command.getFromEncoding();
		String fromDirectory = command.getFromDirectory();
		File fromDir = null;
		File outputDir = null;
		if( fromDirectory != null )
		{
			fromDir = new File( fromDirectory );
		}
		if( outputDirectory != null )
		{
			outputDir = new File( outputDirectory );
		}
		Collection<File> files = null;
		
		if( fromEncoding == null )
		{
			fromEncoding = getDefaultSystemCharset().displayName();
		}
		
		if( StringUtils.isNotEmpty( fileFilterPattern ) )
		{
			if( fromDir == null )
			{
				fromDir = getUserDir();
			}
			files = getFiles( fromDir, fileFilterPattern, command.isRecursiveFileFilter() );
		}
		
		if( files != null )
		{
			if( command.isVerbose() )
			{
				try 
				{
					showFiles( files, fromEncoding );
				}
				catch ( IOException e ) 
				{
					log.error( "Não foi possível ler os arquivos.", e );
				}
			}
			
			if( StringUtils.isNotEmpty( toEncoding ) )
			{
				try 
				{
					new FilesEncodingConverter( fromDir, files, fromEncoding, toEncoding, outputDir ).convert();
				} 
				catch ( IOException e ) 
				{
					log.error( "Não foi possível converter os arquivos.", e );
				}
			}
		}
	}
}
